package client;

/* ActionsClient
 * ---------------------------------------------------
 * Written by
 * Rebecca Van Bussel 6271316
 * October 24, 2013
 */

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextHelper;

import commands.*;

import server.Actions;
import server.ActionsHelper;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Driver {

	private static Actions actions;
	private static String[] serverNames={"SPVM", "SPL", "SPB"};
	private static String officerId;
	private static String station;
	private static LogActionsClient la;
	private static Scanner input = new Scanner(System.in);
	private static Command currentCommand;
	private static ExecutorService service;
	private static String response;
	

	private static Date dNow = new Date( );
	private static SimpleDateFormat ft = new SimpleDateFormat ("E yyyy.MM.dd 'at' hh:mm:ss a zzz");

	public static void main(String[] args) {

		Properties props = new Properties();
		props.put("org.omg.CORBA.ORBInitialPort", "1050");
		props.put("org.omg.CORBA.ORBInitialHost", "SPOKANE");

		System.out.println("WELCOME TO THE REALLY GREAT AND TECHNOLOGICALLY MASTERFUL POLICE SOFTWARE SYSTEM!");
		System.out.println("-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-");

		try {
			while (true) {
				login();

				ORB orb = ORB.init(args, props);

				// get the root naming context
				org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
				NamingContext ncRef = NamingContextHelper.narrow(objRef);

				// resolve the Object Reference in Naming
				NameComponent nc = new NameComponent(station, "");
				NameComponent path[] = {nc};
				actions = ActionsHelper.narrow(ncRef.resolve(path));

				la=new LogActionsClient(officerId);
				la.addAction(ft.format(dNow)+" Officer "+officerId+" logged in");
				actions.startSession(officerId);
				printMenu();
				la.addAction(ft.format(dNow)+" Officer "+officerId+" logged out");
				la.closeFile();
				actions.endSession(officerId);
				
			}
		} catch (Exception e) {
			System.out.println("Error connecting to server. Exiting...");
			e.printStackTrace();
			System.exit(0);
		}
	}

	private static void login() {
		String id="";
		boolean validId=false;
		while (!validId) {

			boolean validStation=false;
			boolean validInt=true;

			System.out.println("Please enter your id or 'Q' to quit: ");
			id=input.nextLine();

			id=id.trim().toUpperCase();

			if (id.length()==1&&id.charAt(0)=='Q') {
				System.out.println("Good bye!");
				System.exit(0);
			}

			if (id.length()<7||id.length()>8) {
				System.out.println("Invalid id. Try again.");
				continue;
			}

			station=id.substring(0, id.length()-4);

			for (int i=0;i<serverNames.length;++i)
				if (station.equals(serverNames[i])) {
					validStation=true;
				}

			for (int i=id.length()-4; i<id.length(); ++i)
				if (id.charAt(i)>'9') {
					validInt=false;
				}

			if (!validStation||!validInt) 
				System.out.println("Invalid id. Try again.");
			else {
				officerId=id;
				validId=true;
			}
		}
	}

	private static void printMenu() {
		boolean showMenu=true;
		while (showMenu) {
			response = "";
			System.out.println();
			System.out.println("What would you like to do?");
			System.out.println("1. Create new missing person record\n"
					+ "2. Create new criminal record\n"
					+ "3. Edit record\n"
					+ "4. Consult record tallies\n"
					+ "5. Populate tables\n"
					+ "6. View all records\n"
					+ "7. View my records\n"
					+ "8. Transfer a record\n"
					+ "9. Logout");

			int choice=Integer.parseInt(input.nextLine());
			switch(choice)
			{
			case 1:
				createRecordPrompt("Missing");
				break;
			case 2:
				createRecordPrompt("Criminal");
				break;
			case 3:
				editRecordPrompt();
				break;
			case 4:
				currentCommand = new GetRecordCounts(actions, officerId);
				doCommand(currentCommand);
				la.addAction(ft.format(dNow)+" Officer "+officerId+" checked record tallies\nServer response: "+response);
				System.out.println(response);
				break;
			case 5:
				populateTables();
				break;
			case 6:
				currentCommand = new GetAllRecords(actions, officerId);
				doCommand(currentCommand);
				la.addAction(ft.format(dNow)+" Officer "+officerId+" viewed all records\nServer response: "+response);
				if (response.equals(""))
					System.out.println("No records at server");
				else
					System.out.println(response);
				break;
			case 7:
				currentCommand = new GetMyRecords(actions, officerId);
				doCommand(currentCommand);
				la.addAction(ft.format(dNow)+" Officer "+officerId+" viewed their personal records\nServer response: "+response);
				if (response.equals(""))
					System.out.println("No records at server");
				else
					System.out.println(response);
				break;
			case 8:
				transferRecordPrompt();
				break;
			case 9:
				showMenu=false;
				break;
			default:
				System.out.println("Invalid Input, please try again.");
			}
		}
	}

	private static void transferRecordPrompt() {
		System.out.println("TRANSFER RECORD");
		System.out.print("Record ID: ");
		String id=input.nextLine();
		id=id.toUpperCase();
		System.out.print("Station: ");
		String station=input.nextLine();
		station=station.toUpperCase();
		System.out.println();
		currentCommand = new TransferRecord(actions, id, station, officerId);
		doCommand(currentCommand);
		la.addAction(ft.format(dNow)+" Officer "+officerId+" entered info to tranfer Record ID "+id
				+" to station "+station+"\nServer response: "+response);
		System.out.println(response);		
	}

	private static void editRecordPrompt() {
		System.out.println("EDIT RECORD");
		System.out.print("Last name: ");
		String lname=input.nextLine();
		String id="";
		System.out.print("ID: ");
		id=input.nextLine();
		id=id.toUpperCase();
		String status=pickStatus();
		System.out.println();
		currentCommand = new EditRecord(actions, lname, id, status, officerId);
		doCommand(currentCommand);
		la.addAction(ft.format(dNow)+" Officer "+officerId+" entered information for new missing person record:\nLast name: "
				+lname+" \nRecord ID: "+id+" \nNew status: "+status+"\nServer response: "+response);
		System.out.println(response);
	}

	private static void createRecordPrompt(String type) {
		System.out.println("CREATE "+type.toUpperCase()+" RECORD");
		System.out.print("First name: ");
		String fname=input.nextLine();
		System.out.print("Last name: ");
		String lname=input.nextLine();
		System.out.print("Address: ");
		String address=input.nextLine();

		boolean dateFormat;
		Date lastDate=null;
		do {
			dateFormat=true;
			System.out.print("Date (Month DD, YYYY): ");
			String date=input.nextLine();
			try {
				lastDate = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(date);
			} catch (Exception e) {
				System.out.println("Wrong date format, use: Month DD, YYYY");
				dateFormat=false;
			}
		} while (!dateFormat);

		System.out.print("Location: ");
		String lastLocation=input.nextLine();
		String status=pickStatus();
		System.out.println();

		if (type.equals("Criminal")) {
			currentCommand = new CreateCriminalRecord(actions, fname, lname, address, lastDate.getTime(), lastLocation, status, officerId);
			doCommand(currentCommand);
			la.addAction(ft.format(dNow)+" Officer "+officerId+" entered information for new criminal record:\nName:"+fname+" "
					+lname+" \nAddress:"+address+" \nDate:"+lastDate+" \nLocation:"+lastLocation+" \nStatus:"+status+"\nServer response: "+response);
			System.out.println(response);
		}
		else {
			currentCommand = new CreateMissingRecord(actions, fname, lname, address, lastDate.getTime(), lastLocation, status, officerId);
			doCommand(currentCommand);
			la.addAction(ft.format(dNow)+"] Officer "+officerId+" entered information for new missing person record:\nName:"+fname+" "
					+lname+" \nAddress:"+address+" \nDate:"+lastDate+" \nLocation:"+lastLocation+" \nStatus:"+status+"\nServer response: "+response);
			System.out.println(response);

		}
	}
	
	private static void doCommand(Command command) {
		service = Executors.newSingleThreadExecutor();
		
		while(true){
			try {
				Runnable r = new Runnable() {
					@Override
					public void run() {
						response = currentCommand.execute();
					}
				};

				Future<?> f = service.submit(r);

				f.get(5, TimeUnit.SECONDS);     // attempt the task for two minutes
			}
			catch (final InterruptedException e) {
				// The thread was interrupted during sleep, wait or join
			}
			catch (final TimeoutException e) {
				doCommand(currentCommand);
			}
			catch (final ExecutionException e) {
				// An exception from within the Runnable task
			}
			
			service.shutdown();
			break; 
		}
	}

	private static String pickStatus() {
		while (true) {
			System.out.println("Select status: ");
			System.out.println("1. Found\n2. Missing\n3. Captured\n4. At large");
			int status=Integer.parseInt(input.nextLine());
			switch(status)
			{
			case 1:
				return "Found";
			case 2:
				return "Missing";
			case 3:
				return "Captured";
			case 4:
				return "At large";
			default:
				System.out.println("Invalid Input, please try again.");
			}
		}
	}

	private static void populateTables() {
		System.out.println("How many missing person records to create? ");
		int missingNum=Integer.parseInt(input.nextLine());
		System.out.println("How many criminal records to create? ");
		int criminalNum=Integer.parseInt(input.nextLine());

		for (int i=0;i<missingNum;++i) {
			InstanceGenerator igm = new InstanceGenerator('M');
			Date date = null;
			try {
				date = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(igm.getDate());
			} catch (ParseException e) {
				e.printStackTrace();
			}
			currentCommand = new CreateMissingRecord(actions, igm.getFname(), igm.getLname(), igm.getAddress(), date.getTime(), igm.getLocation(), igm.getStatus(), officerId);
			doCommand(currentCommand);
			la.addAction(ft.format(dNow)+" Officer "+officerId+" generated information for new missing person record:\nName:"+igm.getFname()+" "
					+igm.getLname()+" \nAddress:"+igm.getAddress()+" \nDate:"+date+" \nLocation:"+igm.getLocation()+" \nStatus:"+igm.getStatus()
					+" \nServer response: "+response);
			System.out.println(response);
		}


		for (int i=0;i<criminalNum;++i) {
			InstanceGenerator igc = new InstanceGenerator('C');
			Date date = null;
			try {
				date = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH).parse(igc.getDate());
			} catch (ParseException e) {
				e.printStackTrace();
			}
			
			currentCommand = new CreateCriminalRecord(actions, igc.getFname(), igc.getLname(), igc.getAddress(), date.getTime(), igc.getLocation(), igc.getStatus(), officerId);
			doCommand(currentCommand);
			
			la.addAction(ft.format(dNow)+" Officer "+officerId+" generated information for new criminal record:\nName:"+igc.getFname()+" "
					+igc.getLname()+" \nAddress:"+igc.getAddress()+" \nDate:"+date+" \nLocation:"+igc.getLocation()+" \nStatus:"+igc.getStatus()
					+"\nServer response: "+response);
			System.out.println(response);

		}

	}

}
